/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frmem.c                                            */
/*  NOTE      =                                                    */
/*  DATE      = 2014/10/10 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "frcomm.h"
#include "frmem_chk.h"

#if FR_MEM_LEAK_CHECK
    //显示所有申请和释放的log 1:需要;0:不需要
    #define APM_SHOW_ALLMEM     0
    #define APM_NAME_MAX        128

    typedef struct __st_memunit
    {
        char str_file[APM_NAME_MAX];    //文件名
        char str_fun[56];               //函数名
        int  i_line_no;                 //行号
        void *p;
        //void (*pf_prt_data)(void* , char *, unsigned int);
        int i_size;
    } Mem_unit;

    /* 节点 */
    typedef struct __HashNode {
        void* p_key;
        Mem_unit* p_value;
        struct __HashNode* next; //相同hash取下一个节点
    } HashNode;

    /* 头节点 */
    typedef struct __Hashtable {
        int i_size;
        int i_item_size;
        HashNode* p_hdr;
    } Hashtable;

    pthread_rwlock_t  rwlock_mem_chk = PTHREAD_RWLOCK_INITIALIZER;
    Hashtable g_ht_mem = {0, 0, NULL};
    unsigned int g_ui_init_times=0;

    /*hash算法*/
    static int __hash(void* p_key, int i_size)
    {
        static char arr_key[64];
        int i_rect = 0, i_idx=0;

        snprintf(arr_key, sizeof(arr_key), "%p", p_key);
        while (arr_key[i_idx] != '\0')
        {
            i_rect += arr_key[i_idx];
            i_idx++;
        }
        return i_rect % i_size;
    }

    /*初始化hashtable*/
    static Hashtable* __hash_init(Hashtable *p_hash, int i_size)
    {
        p_hash->i_size = i_size;
        p_hash->i_item_size = 0;

        HashNode *head = (HashNode *)calloc(i_size, sizeof(HashNode));
        p_hash->p_hdr = head;
        return p_hash;
    }

    /*添加一个*/
    static void __hash_put(Hashtable *p_hash, void *p_key, Mem_unit *p_value)
    {
        int i_index;
        HashNode *p_hashNode;

        i_index = __hash(p_key, p_hash->i_size);
        p_hashNode = p_hash->p_hdr + i_index;

        while (1)
        {
            if (p_hashNode->p_key == NULL || p_key == (p_hashNode->p_key))
            {
                if (p_hashNode->p_key == NULL)
                {
                    p_hash->i_item_size = p_hash->i_item_size + 1;
                }
                p_hashNode->p_key = p_key;
                if(p_hashNode->p_value)
                    free(p_hashNode->p_value);
                p_hashNode->p_value = p_value;
                return;
            }

            if (p_hashNode->next != NULL)
            {
                p_hashNode = p_hashNode->next;
            }
            else
            {
                HashNode *p_newNode = (HashNode*)calloc(1, sizeof(HashNode));
                p_newNode->p_key = p_key;
                p_newNode->p_value = p_value;
                p_newNode->next = NULL;

                p_hashNode->next = p_newNode;
                p_hash->i_item_size += 1;
                return;
            }
        }

        return;
    }

    /*获取一个*/
    //void* __hash_get(Hashtable *p_hash, void* p_key)
    //{
    //    int i_index;
    //    HashNode *p_hashNode;
    //
    //    i_index = __hash(p_key, p_hash->size);
    //    p_hashNode = p_hash->head + i_index;
    //
    //    while (p_hashNode != NULL)
    //    {
    //        if (p_hashNode->key != NULL&&*p_key == *(p_hashNode->key))
    //        {
    //            return p_hashNode->p_st_value;
    //        }
    //        p_hashNode = p_hashNode->next;
    //    }
    //    return NULL;
    //}

    /*删除一个*/
    static int __hash_remove(Hashtable *p_hash, void* p_key)
    {
        int i_index=0;
        HashNode *p_hashNode=NULL, *p_nodeTemp=NULL;

        i_index = __hash(p_key, p_hash->i_size);
        p_hashNode = p_hash->p_hdr + i_index;
        p_nodeTemp = p_hashNode;

        while (p_hashNode != NULL)
        {
            if ( p_key == p_hashNode->p_key )
            {
                if ((p_hash->p_hdr + i_index) == p_hashNode)
                {
                    p_hashNode->p_key = NULL;
                    if(p_hashNode->p_value)
                        free(p_hashNode->p_value);
                    p_hashNode->p_value = NULL;
                }
                else
                {
                    p_nodeTemp->next = p_hashNode->next;
                    if(p_hashNode->p_value)
                        free(p_hashNode->p_value);
                    free(p_hashNode);
                }
                p_hash->i_item_size = p_hash->i_item_size - 1;
                return 1;
            }
            p_nodeTemp = p_hashNode;
            p_hashNode = p_hashNode->next;
        }

        return 0;
    }

    /*打印*/
    static void __hash_show(Hashtable *p_hash)
    {
        HashNode *p_st_node_head = p_hash->p_hdr;
        Mem_unit *p_st_unit = NULL;
        int i_flag_print_idx =0, i_index=0;

        for (i_index = 0; i_index < p_hash->i_size; i_index++)
        {
            i_flag_print_idx = 0;
            if (p_st_node_head->p_key!=NULL)
            {
                i_flag_print_idx = 1;
                printf("index:%d\n", i_index);
                p_st_unit = (Mem_unit *)(p_st_node_head->p_value);
                printf("\t[%s(%s:%d)] size=%d(%p)\n"
                        , p_st_unit->str_fun, p_st_unit->str_file, p_st_unit->i_line_no, p_st_unit->i_size, p_st_unit->p );
            }

            HashNode *next = p_st_node_head->next;
            while (next != NULL)
            {
                if (next->p_key != NULL)
                {
                    if(i_flag_print_idx == 0)
                    {
                        i_flag_print_idx = 1;
                        printf("index:%d\n", i_index);
                    }

                    p_st_unit = (Mem_unit *)(next->p_value);
                    printf("\t[%s(%s:%d)] size=%d(%p)\n"
                            , p_st_unit->str_fun, p_st_unit->str_file, p_st_unit->i_line_no, p_st_unit->i_size, p_st_unit->p);
                }
                next = next->next;
            }
            p_st_node_head++;
        }
    }

    /*销毁*/
    static void __hash_destroy(Hashtable *p_hash)
    {
        if(p_hash == NULL)
            return;

        HashNode *p_node_head = p_hash->p_hdr;
        int i_index=0;

        for (i_index = 0; i_index < p_hash->i_size; i_index++)
        {
            HashNode *p_node_next = p_node_head->next;
            while (p_node_next != NULL)
            {
                HashNode *p_node_temp = p_node_next;
                p_node_next = p_node_next->next;
                if(p_node_temp->p_value)
                    free(p_node_temp->p_value);
                free(p_node_temp);
            }
            p_node_head++;
        }
        free(p_hash->p_hdr);
        //free(p_hash);
    }

    /*******************************************************************************
    *  FUNC     :  缓存池初始化
    *  ARGS     :
    *  RTN      :  FR_SUCC (success)
    *           :  FR_FAIL (failed)
    *  NOTE     :
    *******************************************************************************/
    int __fr_memchk_init(const char *p_name, const char *p_fname, const char *p_fun, int i_lineno)
    {
        //printf("memchk_init %s [%s:%d]\n", p_name ,p_fname, i_lineno);

        __sync_fetch_and_add(&g_ui_init_times, 1);
        if(g_ui_init_times > 1)
            return FR_SUCC;
        __hash_init(&g_ht_mem, 100000);

        return FR_SUCC;
    }

    void* __fr_memchk_init2(const char *p_name, const char *p_fname, const char *p_fun, int i_lineno)
    {
        __fr_memchk_init(p_name, p_fname, p_fun, i_lineno);

        return (void *)1; //使用内存检查时，必须保证fr_slab_set是空函数，使用放回值只有不是0就可以
    }

    /*******************************************************************************
    *  FUNC     :  获取当前缓存池的信息
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_stat(char **pp_outbuff)
    {
        return;
    }

    /*******************************************************************************
    *  FUNC     :  内存分配函数(功能同malloc)
    *  ARGS     :  s_size   (内存空间大小)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_memchk_malloc(size_t s_size, const char *p_fname, const char *p_fun, int i_lineno)
    {
        Mem_unit *p_st_unit = NULL;

        if (s_size <= 0)
            return NULL;

        // 数据长度 > 100M报警告
        if(s_size > 104857600)
            printf("[%s(%s:%u)] size=%lu (input parameter err?)\n", p_fun,p_fname,i_lineno, s_size);

        p_st_unit = (Mem_unit *)malloc(sizeof(Mem_unit));
        if(p_st_unit == NULL)
            return NULL;

        p_st_unit->p = malloc(s_size);
        if(p_st_unit->p == NULL)
        {
            free(p_st_unit);
            return NULL;
        }
        p_st_unit->i_size = s_size;

        p_st_unit->i_line_no = i_lineno;
        snprintf(p_st_unit->str_file, APM_NAME_MAX, "%s", p_fname);
        snprintf(p_st_unit->str_fun, sizeof(p_st_unit->str_fun), "%s", p_fun);

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        __hash_put(&g_ht_mem, p_st_unit->p, p_st_unit);
        pthread_rwlock_unlock(&rwlock_mem_chk);

    #if APM_SHOW_ALLMEM
        printf("malloc[%s:%d]size=%d paddr=%p\n", p_fname, i_lineno, s_size, p_st_unit->p);
    #endif
        return p_st_unit->p;
    }

    /*******************************************************************************
    *  FUNC     :  字符串拷贝，并重新生成内存空间(功能同strdup)
    *  ARGS     :  p_src    (拷贝字符串地址)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    char *__fr_memchk_strdup(const char *p_src, const char *p_fname, const char *p_fun, int i_lineno)
    {
        char *p_data = NULL;
        Mem_unit *p_st_unit = NULL;

        if (p_src == NULL)
            return NULL;

        p_st_unit = (Mem_unit *)malloc(sizeof(Mem_unit));
        if(p_st_unit == NULL)
            return NULL;

        p_data = strdup(p_src);
        if(p_data == NULL)
        {
            free(p_st_unit);
            return NULL;
        }

        p_st_unit->p = p_data;
        p_st_unit->i_size = strlen(p_src)+1;

        p_st_unit->i_line_no = i_lineno;
        strncpy(p_st_unit->str_file, p_fname, APM_NAME_MAX);
        strncpy(p_st_unit->str_fun, p_fun,sizeof(p_st_unit->str_fun));

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        __hash_put(&g_ht_mem, p_st_unit->p, p_st_unit);
        pthread_rwlock_unlock(&rwlock_mem_chk);

    #if APM_SHOW_ALLMEM
        printf("strdup[%s:%d] paddr=%p\n", p_fname, i_lineno, p_st_unit->p);
    #endif
        return (char *) (p_st_unit->p);
    }

    /*******************************************************************************
    *  FUNC     :  内存数据拷贝，并重新生成内存空间
    *  ARGS     :  p_src    (拷贝数据串地址)
                :  s_size   (拷贝数据大小)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_memchk_memdup(const void *p_src, size_t s_size, const char *p_fname, const char *p_fun, int i_lineno)
    {
        void *p_data;

        if (s_size <= 0)
            return NULL;

        p_data = __fr_memchk_malloc(s_size, p_fname, p_fun, i_lineno);
        if(!p_data)
            return NULL;

        memcpy(p_data, p_src, s_size);
        return p_data;
    }

    /*******************************************************************************
    *  FUNC     :  内存释放函数(功能同free)
    *  ARGS     :  p_free_data (释放地址)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_free(void *p_free_data, const char *p_fname, const char *p_fun, int i_lineno)
    {
        int i_free = 0;

        if (p_free_data == NULL)
            return;

    #if APM_SHOW_ALLMEM
        printf("free[%s:%d] paddr=%p\n", p_fname, i_lineno, p_free_data);
    #endif

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        i_free = __hash_remove(&g_ht_mem, p_free_data);
        pthread_rwlock_unlock(&rwlock_mem_chk);

        if(i_free)
            free(p_free_data);
        else
        {
            printf("already free?[%s:%d] paddr=%p\n", p_fname, i_lineno, p_free_data);
            //可能引起 double free
            free(p_free_data);
        }

        return;
    }

    /*******************************************************************************
    *  FUNC     :  内存检查结束
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_destroy(const char *p_fname, const char *p_fun, int i_lineno)
    {
        __sync_fetch_and_sub(&g_ui_init_times, 1);
        if(g_ui_init_times > 0)
            return;
        printf("memory leak check start...\n");
        __hash_show(&g_ht_mem);
        printf("memory leak check end\n");
        __hash_destroy(&g_ht_mem);

        return;
    }
#endif
